/**
 *
 */
package barrier.senseBarrier;

import utils.Backoff;
import barrier.Barrier;
import barrier.ThreadID;

/**
 * @author Yoni Seroussi
 * @author Roei Raviv
 * 
 * Use an array of size n (number if threads), each cell is used to
 * notify the previous thread that this thread finished it's work for
 * current phase (thread i notifies thread i-1 for all 0<i<N)
 * 
 * Padding does not help here since we update next/prev thread
 */
public class BaseLinearSenseBarrierVolatile implements Barrier {
    // AtomicInteger _count; // how many threads have arrived XXX should not use
    // it.
    private int _size; // number of threads
    private volatile boolean _sense; // object's sense

    // This is the diff between both impls
    private volatile boolean _threadDoneArray[]; // each thread signals next
    // cell when it is done
    private final boolean _useExponentialBackoff;
    private Backoff _backoff; // exponential backoff
    private ThreadLocal<Boolean> _threadSense;
    // thread-local sense, saves access to AtomicInt
    private ThreadLocal<Integer> _threadId;

    /**
     * Constructor
     * 
     * @param n barrier width
     */
    public BaseLinearSenseBarrierVolatile(int n, boolean useExpBackoff) {
        // _count = new AtomicInteger(n);
        _useExponentialBackoff = useExpBackoff;
        if (useExpBackoff) {
            _backoff = new Backoff(Backoff.MIN_BACKOFF, Backoff.MAX_BACKOFF);
        }
        _size = n;
        _sense = false;
        _threadId = new ThreadLocal<Integer>() {
            @Override
            protected Integer initialValue() {
                return ThreadID.get() % _size;
            }
        };
        _threadSense = new ThreadLocal<Boolean>() {
            @Override
            protected Boolean initialValue() {
                return !_sense;
            };
        };
        _threadDoneArray = new boolean[n];
        for (int i = (n - 1); i >= 0; i--) { // all threads not done when we
            // start.
            _threadDoneArray[i] = false;
        }
    }

    /**
     * Wait for threads to catch up.
     */
    @Override
    public void await() {
        // if (_size == 1) {
        // return;
        // }
        int me = _threadId.get();
        boolean mySense = _threadSense.get();

        if (me == (_size - 1)) { // im first - no need to wait for prev
            // mark ready and wait on sense
            _threadDoneArray[me] = true;
// XXX Debug           System.out.println("first updated true, sense " + mySense);
        } else {
            // wait for prev to finish
            int prevIdx = me + 1;
//  XXX Debug          System.out.println(me + " is waiting for prev: " + prevIdx);
            while (!_threadDoneArray[prevIdx]) { // wait until my pred is done.
                if (_useExponentialBackoff) {
                    try {
                        _backoff.backoff();
//                        Thread.yield();
                    } catch (InterruptedException e) {
                        System.out.println("Backoff sleep interrupted");
                    }
                }
            }
            // System.out.println("\t\t\t\t\t my prev " + prevIdx +
            // " updated true");

            if (me == 0) { // im last
                // insure all isDone marks are reset.
                for (int i = (_size - 1); i > 0; i--) { // one thread that works
                    // more - but it never
                    // waits on the volatile
// XXX Debug System.out.println("last sets " + i + " to false");
                    _threadDoneArray[i] = false;
                }
// XXX Debug System.out.println("last returns (sense = " + _sense + " my sense = " + mySense);
                _sense = mySense; // end phase - release all threads
                _threadSense.set(!mySense); // reset my sense for next phase
                if (_useExponentialBackoff) {
                    _backoff.resetLimit();
                }
                
// XXX Debug System.out.println("last mySense: " + _threadSense.get() + " globalSense: " + mySense);
                return; // last doesn't need to update the isDone array.
            } else {
                _threadDoneArray[me] = true; // mark that im done
                // XXX might be better to put before the if (me==0),
                // XXX but then the last would update with no real point.
                
// XXX Debug System.out.println(me + " updated true, my sense is " + mySense);
            }
        }
        // all but last should:
        while (_sense != mySense) {
        } // busy-wait

        _threadSense.set(!mySense); // reset my sense for next phase
    }
}
